package uk.co.hs.util.version;

import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.jar.Attributes;
import java.util.jar.Manifest;

import javax.annotation.PostConstruct;
import javax.servlet.ServletContext;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.io.Resource;
import org.springframework.jmx.export.annotation.ManagedAttribute;
import org.springframework.stereotype.Component;
import org.springframework.web.context.ServletContextAware;

/**
 * Extracts the version of the code and returns it
 */
@Component(value = "hs_versionRetriever")
public class ApplicationVersionRetriever implements VersionRetriever, ApplicationContextAware, ServletContextAware
{
  private ApplicationContext mCtx;

  private ServletContext mServletContext;

  private static final String UNKNOWN = "unknown";

  private String mVersion = UNKNOWN;

  private String mAppName = UNKNOWN;

  private String mFullVersion;

  private String mDBVersion;

  private static final String IMPLEMENTATION_VERSION = "Implementation-Version";

  private static final String IMPLEMENTATION_TITLE = "Implementation-Title";

  private Logger mLogger = LoggerFactory.getLogger(getClass());

  private final static String SPACES = "                                        ";
  private final static String LONG_ASTERISK = "  ***********************************************************************";
  private final static String SPACE = "";
  private final static String ASTERISK = "  *                                                                     *";
  private final static String ABORTED = "  * START ABORTED. See DATABASE_VERSION in System Parameters Table      *";


  /**
   * Initialisation method
   */
  @PostConstruct
  public void init()
  {
    /*SystemParameter sp = mSystemParameterService.getByName(SystemParameter.DATABASE_VERSION);
    if (sp == null)
    {
      mDBVersion = UNKNOWN;
    }
    else
    {
      mDBVersion = sp.getValue();
      if (mDBVersion == null)
      {
        mDBVersion = UNKNOWN;
      }
      else
      {
        mDBVersion = mDBVersion.trim();
        if ("".equals(mDBVersion))
        {
          mDBVersion = UNKNOWN;
        }
      }
    }*/

    Resource manifestResource = null;
    try
    {
      URL url = mServletContext.getResource("/META-INF/MANIFEST.MF");
      manifestResource = mCtx.getResource(url.toString());

      mAppName = getAttribute(manifestResource, IMPLEMENTATION_TITLE);
      if ((mAppName == null) || mAppName.equals(""))
      {
        mAppName = UNKNOWN;
      }
      else
      {
        mAppName = mAppName.trim();
        if ("".equals(mAppName))
        {
          mAppName = UNKNOWN;
        }
      }
      mVersion = getAttribute(manifestResource, IMPLEMENTATION_VERSION);
      if ((mVersion == null) || ("".equals(mVersion)))
      {
        mVersion = UNKNOWN;
      }
      else
      {
        mVersion = mVersion.trim();
        if ("".equals(mVersion))
        {
          mVersion = UNKNOWN;
        }
      }

      mFullVersion = mAppName + " " + mVersion;
    }
    catch (MalformedURLException e)
    {
      e.printStackTrace();
      mFullVersion = UNKNOWN + " " + UNKNOWN;
    }
    catch (IOException ioe)
    {
      mLogger.warn(ioe.getMessage());
      mFullVersion = UNKNOWN + " " + UNKNOWN;
    }
  }

  public void writeVersions()
  {
    mLogger.info("{} SOFTWARE VERSION {}", mAppName, mVersion);
    //mLogger.info("{} DATABASE VERSION {}", mAppName, mDBVersion);

    /*ApplicationVersionLog logEntry = new ApplicationVersionLog();
    logEntry.setAppName(mAppName);
    logEntry.setSoftwareVersion(mVersion);
    logEntry.setDbVersion(mDBVersion);
    logEntry.setUpdatedDateTime(new DateTime());*/

    //mApplicationVersionLogService.writeVersions(logEntry);

    // major software has to match DB version unless unknown
    if (mVersion.equals(UNKNOWN))
    {
      return;
    }
    /*int point1 = mVersion.indexOf('.');
    int point2 = -1;
    if (point1 >= 0)
    {
      point2 = mVersion.indexOf('.', point1 + 1);
    }
    if (point2 > 1)
    {
      String majorVersion = mVersion.substring(0, point2);
      if (!majorVersion.equals(mDBVersion))
      {
        mLogger.error(SPACE);
        mLogger.error(LONG_ASTERISK);
        mLogger.error(ASTERISK);
        String msg = "  * DATABASE VERSION IS " + mDBVersion + ", SOFTWARE VERSION IS " + mVersion;
        int fill = ASTERISK.length() - msg.length() - 1;
        mLogger.error(new StringBuilder(msg).append(SPACES.substring(0, fill)).append("*|").toString());
        mLogger.error(ASTERISK);
        mLogger.error(ABORTED);
        mLogger.error(ASTERISK);
        mLogger.error(LONG_ASTERISK);
        mLogger.error(SPACE);

        try
        {
          Thread.sleep(8000);
        }
        catch (InterruptedException e)
        {
          Thread.currentThread().interrupt();
        }
        throw new RuntimeException("Required database version is " + mDBVersion + ", actual version is " + mVersion);
      }
    }
*/
  }

  /**
   * @see uk.co.hs.util.version.VersionRetriever#getVersion()
   *      {@inheritDoc}
   */
  @ManagedAttribute
  public String getStringVersion()
  {
    return mFullVersion;
  }

  public String getVersion()
  {
    return mVersion;
  }

  /**
   * @see org.springframework.context.ApplicationContextAware
   *      #setApplicationContext
   *      (org.springframework.context.ApplicationContext)
   *      {@inheritDoc}
   */
  public void setApplicationContext(ApplicationContext aArg0)
  {
    mCtx = aArg0;
  }

  private String getAttribute(Resource aManifestResource,
                              String aAttribute) throws IOException
  {
    Manifest manifest = null;

    InputStream stream = aManifestResource.getInputStream();
    if (stream != null)
    {
      manifest = new Manifest(stream);
      Attributes atts = manifest.getMainAttributes();
      return atts.getValue(aAttribute);

    }
    return "";
  }

  /**
   * @see org.springframework.web.context.ServletContextAware
   * #setServletContext(javax.servlet.ServletContext)
   * {@inheritDoc}
   */
  public void setServletContext(ServletContext aServletContext)
  {
    mServletContext = aServletContext;
  }
}
